//
//  MIPData.m
//  V3d_Cocoa
//
//  Created by Y my on 11-4-20.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "MIPData.h"

@interface MIPData ()
// removal /////////////////////////////
- (void)setupRawCells; // 给每个rawCell的元素填入其Cmax
- (void)removeCellsAtDirectionVX:(char)vx VY:(char)vy VZ:(char)vz
				 FromBeginPointX:(unsigned short)begX Y:(unsigned short)begY Z:(unsigned short)begZ
					 ToEndPointX:(unsigned short)endX Y:(unsigned short)endY Z:(unsigned short)endZ;
- (void)setupCellsForCellList:(char)listIndex; // 构造cells数组

// sorting ////////////////////////////
- (void)histogram_sort_ForCellList:(char)listIndex; // 排序剩下的所有Cell
- (void)histogram_sort_WithCmin_From:(unsigned int)begIndex To:(unsigned int)endIndex;

// compress data ///////////////////////
// 将cells数组每个元素中的coord提取出来，放到sortedCellCoord数组中，并释放cells数组，以节省空间。Cmax和Cmin已无用
- (void)compressDataToCellList:(char)listIndex; 

// other ///////////////////////////////
- (unsigned char)intensityAtX:(unsigned short)x Y:(unsigned short)y Z:(unsigned short)z; // 取得相应坐标的intensity
- (void)setCell:(Cell*)cell vx:(unsigned short)x vy:(unsigned short)y vz:(unsigned short)z; // 将信息填入一个cell结构中

@end




@implementation MIPData

// Property /////////////////////////////////
@synthesize isRedDisplay, isGreenDisplay, isBlueDisplay;
@synthesize channelCount;
@synthesize x_len, y_len, z_len;
@synthesize x_cell_len, y_cell_len, z_cell_len;
@synthesize rawData;
#ifdef _TEST_
@synthesize testData;
#endif

- (void)setIsRedDisplay:(bool)r
{
	isRedDisplay = r;
	
	[self notify];
}
- (void)setIsGreenDisplay:(bool)g
{
	isRedDisplay = g;
	
	[self notify];
}
- (void)setIsBlueDisplay:(bool)b
{
	isRedDisplay = b;
	
	[self notify];
}


// public ////////////////////////////
- (id)initWithRawData:(unsigned char****)data
			 X_LENGTH:(unsigned short)x_length
			 Y_LENGTH:(unsigned short)y_length
			 Z_LENGTH:(unsigned short)z_length
		 ChannelCount:(unsigned char)chanCount
{
	self = [super init];
	if (self)
	{
		rawData = data;
		x_len = x_length;
		y_len = y_length;
		z_len = z_length;
		channelCount = chanCount;
		
#ifdef _TEST_
		x_len = y_len = z_len = 33;
		testData = (unsigned char*) malloc(x_len * y_len * z_len * sizeof(unsigned char));
		unsigned char* tmp = testData;
		
		for (short i = 0; i < z_len; i++)
		{
			for (short j = 0; j < y_len; j++)
			{
				for (short t = 0; t < x_len; t++)
				{
					*tmp = 100;
					tmp++;
				}
			}
		}
		channelCount = 1;
		
#define TESTDATA(x, y, z) testData[z * x_len * y_len + y * x_len + x]
		for (short i = z_len-20; i < z_len; i++)
		{
			for (short j = y_len-20; j < y_len; j++)
			{
				for (short t = x_len-20; t < x_len; t++)
				{
					TESTDATA(t, j, i) = 200;
				}
			}
		}
		
#endif
		x_cell_len = x_len - 1;
		y_cell_len = y_len - 1;
		z_cell_len = z_len - 1;
		
		isRedDisplay = isGreenDisplay = isBlueDisplay = true;
	}
	return self;
}


//----- 投影用的Cell ----------------------------------
//         v6                 v7         
//          / -----------------|                      
//        / |                / |                      
//      /__________________/   |        
//   v4 |   |           v5 |   |        
//      |   |              |   |        
//      |   |_ _ _ _ _ _ _ | _ |                      
//      |  / v2            |  / v3      z    y                              
//      |/_________________|/           |  /                      
//     v0                  v1           |/____ x      
//----------------------------------------------------
- (void)proceed
{
	totalCellCount[0] = totalCellCount[1] = totalCellCount[2] = totalCellCount[3] = 0;
	totalBucketCount[0] = totalBucketCount[1] = totalBucketCount[2] = totalBucketCount[3] = 0;
	unsigned int cellCount = x_cell_len * y_cell_len * z_cell_len;
	rawCells = (RawCell*) malloc(sizeof(RawCell) * cellCount);
	isCellAlive = (char*) malloc(sizeof(char) * cellCount);
	
	[self setupRawCells];

	// 按四个方向设置Cell列表
	char vx = 1, vy = 1, vz = 1; // 增长方向，取值范围为{-1, 1}
	unsigned short begX = 0, begY = 0, begZ = 0;
	unsigned short endX = x_cell_len-1, endY = y_cell_len-1, endZ = z_cell_len-1;
	for (char i = 0; i < 4; i++)
	{
		switch (i)
		{
			case 0:
				vx = vy = vz = 1;
				begX = begY = begZ = 0;
				endX = x_cell_len-1; endY = y_cell_len-1; endZ = z_cell_len-1;
				break;
			case 1:
				vx = -1; vy = vz = 1;
				begX = x_cell_len-1; begY = begZ = 0;
				endX = 0; endY = y_cell_len-1; endZ = z_cell_len-1;
				break;
			case 2:
				vx = 1; vy = -1; vz = 1;
				begX = 0; begY = y_cell_len-1; begZ = 0;
				endX = x_cell_len-1; endY = 0; endZ = z_cell_len-1;
				break;
			case 3:
				vx = vy = -1; vz = 1;
				begX = x_cell_len-1; begY = y_cell_len-1; begZ = 0;
				endX = endY = 0; endZ = z_cell_len-1;
				break;
			default:
				break;
		}
		[self removeCellsAtDirectionVX:vx VY:vy VZ:vz
					   FromBeginPointX:begX Y:begY Z:begZ
						   ToEndPointX:endX Y:endY Z:endZ ];
		[self setupCellsForCellList:i];
		[self histogram_sort_ForCellList:i];
		[self compressDataToCellList:i];
	}
	
	
	free(rawCells);
	free(isCellAlive);
	free(cells);
}

- (void)getDataResultByRotationMatrix:(Matrix4*)matRotate
				 out_TotalBucketCount:(unsigned short*)bucketCount
				 out_SortedBucketInfo:(Bucket**)bucketInfos
				   out_TotalCellCount:(unsigned int*)cellCount
				  out_SortedCellCoord:(unsigned int**)cellCoords
{
	Point3D* point[8];
	for (char i = 0; i < 8; i++)
	{
		point[i] = [[Point3D alloc] init];
	}
	point[1].x = 1;
	point[2].y = 1;
	point[3].x = point[3].y = 1;
	point[4].z = 1;
	point[5].x = point[5].z = 1;
	point[6].y = point[6].z = 1;
	point[7].x = point[7].y = point[7].z = 1;
	for (char i = 0; i < 8; i++)
	{
		[point[i] multiple:matRotate];
	}
	
	// 计算z值最大的点(即最高的点)的索引
	char highestPointIndex = 0;
	float highestZ = point[0].z;
	for (char i = 1; i < 8; i++)
	{
		if (highestZ < point[i].z)
		{
			highestZ = point[i].z;
			highestPointIndex = i;
		}
	}
	
	// 选择合适的Cell列表
	char index;
	switch (highestPointIndex)
	{
		case 0:case 7:
			index = 0;
			break;
		case 1:case 6:
			index = 1;
			break;
		case 2:case 5:
			index = 2;
			break;
		case 3:case 4:
			index = 3;
			break;
		default:
			break;
	}
	*bucketCount = totalBucketCount[index];
	*bucketInfos = sortedBucketInfo[index];
	*cellCount = totalCellCount[index];
	*cellCoords = sortedCellCoord[index];
}

- (void)dealloc
{
	for (char i = 0; i < 4; i++)
	{
		free(sortedBucketInfo[i]);
		free(sortedCellCoord[i]);
	}
	
	
	[super dealloc];
}

// private - removal ////////////////////////

//#define CELL(x, y, z) rawCells[(z) * x_cell_len * y_cell_len + (y) * x_cell_len + (x)]
//#define IS_CELL_ALIVE(x, y, z) isCellAlive[(z) * x_cell_len * y_cell_len + (y) * x_cell_len + (x)]

- (void)setupRawCells
{
	NSLog(@"Begin setupRawCells");
	unsigned int cellCount = x_cell_len * y_cell_len * z_cell_len;
	for (unsigned int i = 0; i < cellCount; i++)
	{
		rawCells[i].Cmax = 0;
		rawCells[i].Cmin = 0xff;
	}
	
	unsigned char C;
	unsigned short x, y, z;
	unsigned int tmpIndex;
	for (z = 0; z < z_len; z++)
	{
		for (y = 0; y < y_len; y++)
		{
			for (x = 0; x < x_len; x++)
			{
				C = [self intensityAtX:x Y:y Z:z];
				
				if (z > 0)
				{
					if (x > 0 && y > 0)
					{
						tmpIndex = (z-1) * x_cell_len * y_cell_len + (y-1) * x_cell_len + (x-1);
						if (rawCells[tmpIndex].Cmax < C) rawCells[tmpIndex].Cmax = C;
						if (rawCells[tmpIndex].Cmin > C) rawCells[tmpIndex].Cmin = C;
					}
					
					if (x < x_cell_len && y > 0)
					{
						tmpIndex = (z-1) * x_cell_len * y_cell_len + (y-1) * x_cell_len + x;
						if (rawCells[tmpIndex].Cmax < C) rawCells[tmpIndex].Cmax = C;
						if (rawCells[tmpIndex].Cmin > C) rawCells[tmpIndex].Cmin = C;
					}
					
					if (x > 0 && y < y_cell_len)
					{
						tmpIndex = (z-1) * x_cell_len * y_cell_len + y * x_cell_len + (x-1);
						if (rawCells[tmpIndex].Cmax < C) rawCells[tmpIndex].Cmax = C;
						if (rawCells[tmpIndex].Cmin > C) rawCells[tmpIndex].Cmin = C;
					}
					
					if (x < x_cell_len && y < y_cell_len)
					{
						tmpIndex = (z-1) * x_cell_len * y_cell_len + y * x_cell_len + x;
						if (rawCells[tmpIndex].Cmax < C) rawCells[tmpIndex].Cmax = C;
						if (rawCells[tmpIndex].Cmin > C) rawCells[tmpIndex].Cmin = C;
					}
				}
				
				if (z < z_cell_len)
				{
					if (x > 0 && y > 0)
					{
						tmpIndex = z * x_cell_len * y_cell_len + (y-1) * x_cell_len + (x-1);
						if (rawCells[tmpIndex].Cmax < C) rawCells[tmpIndex].Cmax = C;
						if (rawCells[tmpIndex].Cmin > C) rawCells[tmpIndex].Cmin = C;
					}
					
					if (x < x_cell_len && y > 0)
					{
						tmpIndex = z * x_cell_len * y_cell_len + (y-1) * x_cell_len + x;
						if (rawCells[tmpIndex].Cmax < C) rawCells[tmpIndex].Cmax = C;
						if (rawCells[tmpIndex].Cmin > C) rawCells[tmpIndex].Cmin = C;
					}
					
					if (x > 0 && y < y_cell_len)
					{
						tmpIndex = z * x_cell_len * y_cell_len + y * x_cell_len + (x-1);
						if (rawCells[tmpIndex].Cmax < C) rawCells[tmpIndex].Cmax = C;
						if (rawCells[tmpIndex].Cmin > C) rawCells[tmpIndex].Cmin = C;
					}
					
					if (x < x_cell_len && y < y_cell_len)
					{
						tmpIndex = z * x_cell_len * y_cell_len + y * x_cell_len + x;
						if (rawCells[tmpIndex].Cmax < C) rawCells[tmpIndex].Cmax = C;
						if (rawCells[tmpIndex].Cmin > C) rawCells[tmpIndex].Cmin = C;
					}
				}
			} // for x
		} // for y
	} // for z
}

- (void)removeCellsAtDirectionVX:(char)vx VY:(char)vy VZ:(char)vz
				 FromBeginPointX:(unsigned short)begX Y:(unsigned short)begY Z:(unsigned short)begZ
					 ToEndPointX:(unsigned short)endX Y:(unsigned short)endY Z:(unsigned short)endZ
{
	NSLog(@"Begin remove cells");
	
	memset(isCellAlive, 3, sizeof(char) * (x_cell_len * y_cell_len * z_cell_len));
	
	unsigned char currCmax;
	unsigned int tmpIndex;
	for (char i = 1; i <= 2; i++)  // 来回个扫描一次，两次扫描能更有效地移除Cell
	{
		
		for (unsigned short z = begZ;; z += vz)
		{
			for (unsigned short y = begY;; y += vy)
			{
				for (unsigned short x = begX;; x += vx)
				{
					if (i == 2 && isCellAlive[z * x_cell_len * y_cell_len + y * x_cell_len + x] <= 0)
					{
						if (x == endX)
						{
							break;
						}
						
						continue; // 第二遍扫描时，若该Cell已死，则直接跳到下一个Cell
					}
					
					currCmax = rawCells[z * x_cell_len * y_cell_len + y * x_cell_len + x].Cmax;
					if (i == 1) // 第一遍扫描需要去掉相等的Cell，而第二遍不能，否则会连有效Cell也删去
					{
						tmpIndex = z * x_cell_len * y_cell_len + y * x_cell_len + (x+vx);
						if (x != endX && currCmax >= rawCells[tmpIndex].Cmax) isCellAlive[tmpIndex]--;
						
						tmpIndex = z * x_cell_len * y_cell_len + (y+vy) * x_cell_len + x;
						if (y != endY && currCmax >= rawCells[tmpIndex].Cmax) isCellAlive[tmpIndex]--;
						
						tmpIndex = (z+vz) * x_cell_len * y_cell_len + y * x_cell_len + x;
						if (z != endZ && currCmax >= rawCells[tmpIndex].Cmax) isCellAlive[tmpIndex]--;
					}
					if (i == 2)
					{
						tmpIndex = z * x_cell_len * y_cell_len + y * x_cell_len + (x+vx);
						if (x != endX && currCmax > rawCells[tmpIndex].Cmax) isCellAlive[tmpIndex]--;
						
						tmpIndex = z * x_cell_len * y_cell_len + (y+vy) * x_cell_len + x;
						if (y != endY && currCmax > rawCells[tmpIndex].Cmax) isCellAlive[tmpIndex]--;
						
						tmpIndex = (z+vz) * x_cell_len * y_cell_len + y * x_cell_len + x;
						if (z != endZ && currCmax > rawCells[tmpIndex].Cmax) isCellAlive[tmpIndex]--;
					}
					
					if (x == endX)
					{
						break;
					}
				}
				
				if (y == endY)
				{
					break;
				}
			}
			
			if (z == endZ) 
			{
				break;
			}
				
		}
		
		if (i == 2)
		{
			break;
		}
		
		unsigned int count = x_cell_len * y_cell_len * z_cell_len;
		for (unsigned int c = 0; c < count; c++)
		{
			if (isCellAlive[c] > 0)
			{
				isCellAlive[c] = 3;
			}
		}
		unsigned short tmp;
#define EXCHANGE(a, b) tmp = (a); (a) = (b); (b) = tmp;
		EXCHANGE(begX, endX)
		EXCHANGE(begY, endY)
		EXCHANGE(begZ, endZ)
#undef EXCHANGE
		vx = -vx;
		vy = -vy;
		vz = -vz;
	}
}

- (void)setupCellsForCellList:(char)listIndex
{
	NSLog(@"Begin setup cells");
	unsigned short x, y, z;

	// 统计留下的cell的个数
	unsigned int totalCount = x_cell_len * y_cell_len * z_cell_len;
	char* currCellAlive = isCellAlive;
	for (unsigned int i = 0; i < totalCount; i++)
	{
		totalCellCount[listIndex] += (unsigned int) (*currCellAlive > 0);
		currCellAlive++;
	}
	
	// 将留下来的Cell的信息放在数组中
	if (cells == NULL)
	{   // 第一次分配内存
		cells = (Cell*) malloc(sizeof(Cell) * totalCellCount[listIndex]);
		memset(cells, 0, sizeof(Cell) * totalCellCount[listIndex]);
	}
	else if (totalCellCount[listIndex] < totalCellCount[listIndex-1])
	{   // 这次需要的内存空间比上次少或者与上次相等，就无需重新分配了
		memset(cells, 0, sizeof(Cell) * totalCellCount[listIndex-1]);
	}
	else
	{	// 这次需要的内存比上次的多，需要重新分配内存
		free(cells);
		cells = (Cell*) malloc(sizeof(Cell) * totalCellCount[listIndex]);
		memset(cells, 0, sizeof(Cell) * totalCellCount[listIndex]);
	}

	
	char* tmpIsCellAlive = isCellAlive;
	Cell* pCell = cells;
	for (z = 0; z < z_cell_len; z++)
	{
		for (y = 0; y < y_cell_len; y++)
		{
			for (x = 0; x < x_cell_len; x++)
			{
				if (*tmpIsCellAlive > 0)
				{
					[self setCell:pCell vx:x vy:y vz:z];
					pCell++;
				}
				tmpIsCellAlive++;
			}
		}
	}
	
}


// private - sorting /////////////////
- (void)histogram_sort_ForCellList:(char)listIndex
{
	NSLog(@"Begin histogram sort");
	unsigned int countArray[256];
	memset(countArray, 0, sizeof(unsigned int) * 256);
	
	unsigned int tmpBucketArray[256];  // 只在函数内部使用,方便 pass-2 的操作
	memset(tmpBucketArray, -1, sizeof(unsigned int) * 256);

	// pass 1 : 计算每个bucket包含多少元素
	NSLog(@"pass1");
	for (unsigned int i = 0; i < totalCellCount[listIndex]; i++)
	{
		if (countArray[ cells[i].Cmax ] == 0)
		{
			totalBucketCount[listIndex]++;
		}
		countArray[ cells[i].Cmax ]++;
	}
	sortedBucketInfo[listIndex] = (Bucket*) malloc(sizeof(Bucket) * totalBucketCount[listIndex]);
	memset(sortedBucketInfo[listIndex], 0, sizeof(Bucket) * totalBucketCount[listIndex]);
	
	// 设置索引列表，降序排列
	short bucketIndex = totalBucketCount[listIndex] - 1;
	unsigned int cellIndex = totalCellCount[listIndex];
	short currBucketCount = 0;
	for (short bucketVal = 0; bucketVal < 256; bucketVal++)
	{
		if (countArray[bucketVal] != 0)
		{
			cellIndex -= countArray[bucketVal];
			sortedBucketInfo[listIndex][bucketIndex].Cmax = bucketVal;
			sortedBucketInfo[listIndex][bucketIndex].beginIndex = cellIndex;
			bucketIndex--;
			tmpBucketArray[bucketVal] = cellIndex;
			currBucketCount++;
		}
		if (currBucketCount == totalBucketCount[listIndex])
		{
			break;
		}
	}
	
	// pass 2 : 将元素填入各个bucket
	NSLog(@"pass2");
	BOOL* sorted = (BOOL*) malloc(sizeof(BOOL) * totalCellCount[listIndex]);
	memset(sorted, 0, sizeof(BOOL) * totalCellCount[listIndex]);
	BOOL* tmpSorted = sorted;
	Cell tmpCell;
	Cell exchangeBuf;
	unsigned int desIndex;
	unsigned int currCellCount = 0;
	for (unsigned int i = 0; i < totalCellCount[listIndex]; i++)
	{
		if (!(*tmpSorted))
		{
			tmpCell = cells[i];
			cells[i].Cmax = 0;
			cells[i].Cmin = 1;
			while (tmpCell.Cmax >= tmpCell.Cmin)
			{
				desIndex = tmpBucketArray[tmpCell.Cmax] + (--countArray[tmpCell.Cmax]);
				exchangeBuf = tmpCell; tmpCell = cells[desIndex]; cells[desIndex] = exchangeBuf;
				sorted[desIndex] = YES;
				currCellCount++;
			}
		}
		tmpSorted++;
		if (currCellCount == totalCellCount[listIndex])
		{
			break;
		}
	}
	
	free(sorted);
	
	// pass 3 : 将每个bucket排序, 降序
	// 使用插入排序
	NSLog(@"pass3");
	unsigned int begin, end;
	for (unsigned short i = 0; i < totalBucketCount[listIndex]; i++)
	{
		begin = sortedBucketInfo[listIndex][i].beginIndex;
		end = (i == totalBucketCount[listIndex]-1) ? totalCellCount[listIndex] : sortedBucketInfo[listIndex][i+1].beginIndex;
		[self histogram_sort_WithCmin_From:begin To:end];
	}
	
}

- (void)histogram_sort_WithCmin_From:(unsigned int)begIndex To:(unsigned int)endIndex
{
	unsigned int countArray[256];
	memset(countArray, 0, sizeof(unsigned int) * 256);
	
	unsigned int tmpBucketArray[256];  // 只在函数内部使用,方便 pass-2 的操作
	memset(tmpBucketArray, 0xff, sizeof(unsigned int) * 256);
	
	unsigned int cellCount = endIndex - begIndex;
	
	// pass 1 : 计算每个bucket包含多少元素
	unsigned char bucketCount = 0;
	for (unsigned int i = begIndex; i < endIndex; i++)
	{
		if (countArray[ cells[i].Cmin ] == 0)
		{
			bucketCount++;
		}
		countArray[ cells[i].Cmin ]++;
	}
	
	// 设置索引列表，降序排列
	unsigned int cellIndex = endIndex;
	short currBucketCount = 0;
	for (short bucketVal = 0; bucketVal < 256; bucketVal++)
	{
		if (countArray[bucketVal] != 0)
		{
			cellIndex -= countArray[bucketVal];
			tmpBucketArray[bucketVal] = cellIndex;
			currBucketCount++;
		}
		if (currBucketCount == bucketCount)
		{
			break;
		}
	}
	
	// pass 2 : 将元素填入各个bucket
	BOOL* sorted = (BOOL*) malloc(sizeof(BOOL) * cellCount);
	memset(sorted, 0, sizeof(BOOL) * cellCount);
	BOOL* tmpSorted = sorted;
	Cell tmpCell;
	Cell exchangeBuf;
	unsigned int desIndex;
	unsigned int currCellCount = 0;
	for (unsigned int i = begIndex; i < endIndex; i++)
	{
		if (!(*tmpSorted))
		{
			tmpCell = cells[i];
			cells[i].Cmax = 0;
			cells[i].Cmin = 1;
			while (tmpCell.Cmax >= tmpCell.Cmin)
			{
				desIndex = tmpBucketArray[tmpCell.Cmin] + (--countArray[tmpCell.Cmin]);
				exchangeBuf = tmpCell; tmpCell = cells[desIndex]; cells[desIndex] = exchangeBuf;
				sorted[desIndex-begIndex] = YES;
				currCellCount++;
			}
		}
		tmpSorted++;
		if (currCellCount == cellCount)
		{
			break;
		}
	}
	
	free(sorted);
}


// private - compress data //////////////////
- (void)compressDataToCellList:(char)listIndex
{
	NSLog(@"Begin compress data");
	sortedCellCoord[listIndex] = (unsigned int*) malloc(sizeof(unsigned int) * totalCellCount[listIndex]);
	for (unsigned int i = 0; i < totalCellCount[listIndex]; i++)
	{
		sortedCellCoord[listIndex][i] = cells[i].coord;
	}
}

// private - other //////////////////////////
- (unsigned char)intensityAtX:(unsigned short)x Y:(unsigned short)y Z:(unsigned short)z
{
	unsigned short intensity = 0;
	
#ifndef _TEST_
	
	unsigned char c = 0;
	if (channelCount == 1)
	{
		intensity = rawData[0][z][y][x];
	}
	else // channelCount == 3
	{
		if (isRedDisplay)
		{
			intensity += rawData[0][z][y][x];
			c++;
		}
		if (isGreenDisplay)
		{
			intensity += rawData[1][z][y][x];
			c++;
		}
		if (isBlueDisplay)
		{
			intensity += rawData[2][z][y][x];
			c++;
		}
		if (c == 0)
		{
			intensity = 0;
		}
		else
		{
			intensity /= c;
		}

	}
#else
	
	unsigned short index = z * x_len * y_len + y * x_len + x;
	intensity += testData[index];
	
#endif
	
	return intensity;
	
}

- (void)setCell:(Cell *)cell vx:(unsigned short)x vy:(unsigned short)y vz:(unsigned short)z
{
	unsigned int coord;

	SET_POSION(x, y, z, coord);
	
	unsigned int index = z * y_cell_len * x_cell_len + y * x_cell_len + x;
	cell->Cmax = rawCells[index].Cmax;
	cell->Cmin = rawCells[index].Cmin;
	cell->coord = coord;
}


@end